﻿using System;
using AutoMapper;
using TelerikMvcGridCustomBindingHelper.Util;

namespace TelerikMvcGridCustomBindingHelper.Mapper
{
    public interface IValueResolver
    {
        object Resolve(ResultContext source);
    }

    public interface IQueryValueResolver : IGlobalValueResolver
    {
        void SetTargetProperty(string targetProperty);
        string GetTargetProperty();
    }

    public abstract class QueryValueResolver<TSource, TDestination> : GlobalValueResolver<TSource, TDestination>, IQueryValueResolver
    {
        private string _targetProperty;

        public void SetTargetProperty(string targetProperty)
        {
            _targetProperty = targetProperty;
        }

        public string GetTargetProperty()
        {
            return _targetProperty;
        }
    }

    public interface IGlobalValueResolver : IValueResolver
    {
        Type GetSourceType();
        Type GetDestinationType();
    }

    public abstract class GlobalValueResolver<TSource, TDestination> : ValueResolver<TSource, TDestination>, IGlobalValueResolver
    {
        public Type GetSourceType()
        {
            return typeof(TSource);
        }

        public Type GetDestinationType()
        {
            return typeof(TDestination);
        }
    }

    public abstract class ValueResolver<TSource, TDestination> : IValueResolver
    {
        public object Resolve(ResultContext source)
        {
            var value = source.Value;

            if (!(source.Value is TSource))
            {
                value = Reflector.TryChangeType(source.Value, typeof(TSource), GridModelMapper.ConfigurationObject.DefaultCulture);
                if (value == null && source.Value != null)
                {
                    throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", typeof(TSource), source.Value.GetType()));
                }
            }

            if (value != null && !(value is TSource))
            {
                throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", typeof(TSource), source.Value.GetType()));
            }

            return ResolveCore((TSource)value);
        }

        protected abstract TDestination ResolveCore(TSource source);
    }

    public class ResultContext
    {
        public readonly Type EntityType;
        public readonly Type ViewModelType;
        public readonly object Value;

        internal ResultContext(Type entityType, Type viewModelType, object value)
        {
            Value = value;
            EntityType = entityType;
            ViewModelType = viewModelType;
        }
    }
}